package models

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/astaxie/beego"
)

type Code2sessionResult struct {
	ErrorCode  int    `json:"errcode"`
	ErrorMsg   string `json:"errmsg,omitempty"`
	SessionKey string `json:"session_key,omitempty"`
	ExpiresIn  int    `json:"expires_in,omitempty"`
	Openid     string `json:"openid,omitempty"`
	Unionid    string `json:"unionid,omitempty"`
}
type WaterMark struct {
	AppID     string `json:"appid"`
	Timestamp int    `json:"timestamp"`
}
type UserInfo struct {
	Openid    string `json:"openid"`
	NickName  string `json:"nickName"`
	Gender    int    `json:"gender"`
	City      string `json:"city"`
	Province  string `json:"province"`
	Country   string `json:"country"`
	AvatarURL string `json:"avatarUrl"`
	UnionID   string `json:"unionId"`
	WaterMark `json:"watermark"`
}

var (
	XappAppid  = beego.AppConfig.String("xapp_appid")
	AppAppID   = beego.AppConfig.String("app_appid")
	XappSecret = beego.AppConfig.String("xapp_secret")
)

func XappLogin(code, encrytedData, iv string) (userInfo UserInfo, err error) {
	//get openid and session_key
	//beego.Debug(code)
	//beego.Debug(encrytedData)
	//beego.Debug(iv)
	url := "https://api.weixin.qq.com/sns/jscode2session?appid=" + XappAppid + "&secret=" + XappSecret + "&js_code=" + code + "&grant_type=authorization_code"
	r, err := http.Get(url)
	if err != nil {
		//auth error
		beego.Debug(err)
		return
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	code2session := Code2sessionResult{}
	err = json.Unmarshal(body, &code2session)
	if err != nil {
		beego.Debug(err)
		return
	}
	if code2session.ErrorCode > 0 {
		err = fmt.Errorf("%d=>%s", code2session.ErrorCode, code2session.ErrorMsg)
		return
	}
	//code2session success,check signature
	//aes decrypt
	decrypted, err := WXBizDataDecrypt(code2session.SessionKey, encrytedData, iv)
	if err != nil {
		beego.Debug(err)
		return
	}
	beego.Debug(string(decrypted))
	err = json.Unmarshal(decrypted, &userInfo)
	if err != nil {
		beego.Debug(err)
		return
	}
	userInfo.Openid = code2session.Openid
	userInfo.UnionID = code2session.Unionid
	return
}

func WXBizDataDecrypt(sessionKey, encryptedData, iv string) (decrypted []byte, err error) {
	var (
		decryptedkey, decryptedIV, decryptedData []byte
	)
	decryptedkey, err = base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return
	}
	decryptedData, err = base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return
	}
	decryptedIV, err = base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return
	}
	block, err := aes.NewCipher(decryptedkey)
	if err != nil {
		beego.Debug(err)
		return
	}
	blockMode := cipher.NewCBCDecrypter(block, decryptedIV)
	decrypted = make([]byte, len(decryptedData))
	// origData := crypted
	blockMode.CryptBlocks(decrypted, decryptedData)
	decrypted = PKCS5UnPadding(decrypted)
	return
}

func WXBizDataSignature(sessionKey, rawData string) string {
	hash := sha1.New()
	_, err := hash.Write([]byte(rawData + sessionKey))
	if err != nil {
		return ""
	}
	sign := hash.Sum(nil)
	return string(sign)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
